﻿using System.Text.RegularExpressions;

namespace Palindromes
{
  /// <summary>
  /// Checks if input string is palindrome
  /// </summary>
  public class PalindromChecker
  {
    #region private vars
    /// <summary>
    /// Input string (string for test).
    /// </summary>
    private string input;
    /// <summary>
    /// Current left cursor position.
    /// </summary>
    private int leftCursorIndex;
    /// <summary>
    /// Current right cursor position.
    /// </summary>
    private int rightCursorIndex;
    /// <summary>
    /// Console printer
    /// </summary>
    private readonly PalindromeConsolePrinter printer;
    #endregion

    #region private methods
    /// <summary>
    /// Makes next step in checking algorithm
    /// </summary>
    /// <returns></returns>
    private bool NextStep()
    {
      leftCursorIndex = FindFirstNonDelimiterFromLeft();
      printer.WriteCurrentLeftCursorPosition(leftCursorIndex);
      if (leftCursorIndex == rightCursorIndex)
      {
        printer.WriteEndWhileShiftingLeftCursor();
        return true;
      }
      rightCursorIndex = FindFirstNonDelimiterFromRight();
      printer.WriteCurrentRightCursorPosition(rightCursorIndex);
      if (leftCursorIndex == rightCursorIndex)
      {
        printer.WriteEndWhileShiftingRightCursor();
        return true;
      }

      return CompareSymbols();
    }

    /// <summary>
    /// Compares two symbols
    /// </summary>
    /// <returns>returns true if symbols equals, false otherwise</returns>
    private bool CompareSymbols()
    {
      printer.WriteComparingSymbols(input, leftCursorIndex, rightCursorIndex);
      return input[leftCursorIndex] == input[rightCursorIndex];
    }

    /// <summary>
    /// Moves left cursor to right until find first non-delimiter
    /// or matches with right cursor
    /// </summary>
    /// <returns>index of first non-delimiter symbol</returns>
    private int FindFirstNonDelimiterFromLeft()
    {
      int currentIndex = leftCursorIndex + 1;
      while (currentIndex < rightCursorIndex)
      {
        printer.WriteShiftingStepFromLeft(currentIndex);

        if (!IsDelimiter(currentIndex))
        {
          return currentIndex;
        }
        currentIndex = currentIndex + 1;
      }
      return rightCursorIndex;
    }

    /// <summary>
    /// Moves right cursor to left until find first non-delimiter
    /// or matches with left cursor
    /// </summary>
    /// <returns>index of first non-delimiter symbol</returns>
    private int FindFirstNonDelimiterFromRight()
    {
      int currentIndex = rightCursorIndex - 1;
      while (leftCursorIndex < currentIndex)
      {
        printer.WriteShiftingStepFromRight(currentIndex);
        if (!IsDelimiter(currentIndex))
        {
          return currentIndex;
        }
        currentIndex = currentIndex - 1;
      }
      return leftCursorIndex;
    }

    /// <summary>
    /// Tests if symbol on index is delimiter
    /// </summary>
    /// <param name="index">index of symbol to be tested</param>
    /// <returns>true, if symbol on specified index is delimiter, false 
    /// otherwise</returns>
    private bool IsDelimiter(int index)
    {
      Regex delimiterRegex = new Regex(@"\W");
      string stringForComparsion = input.Substring(index, 1);
      bool isDelim = delimiterRegex.IsMatch(stringForComparsion);
      if (isDelim)
      {
        printer.WriteSkippingDelimiter(input, index);
      }
      return isDelim;
    }
    #endregion

    #region public properties
    /// <summary>
    /// Property for input string (string to be checked).
    /// </summary>
    public string Input
    {
      set
      {
        input = value != null ? value.Trim().ToLower() : string.Empty;
      }
    }
    #endregion

    #region public methods
    /// <summary>
    /// Constructor.
    /// </summary>
    /// <param name="printer">console printer for message printing</param>
    public PalindromChecker(PalindromeConsolePrinter printer)
    {
      this.printer = printer;
    }

    /// <summary>
    /// Tests if input string matches palindrome.
    /// </summary>
    /// <returns>true if input string matches palindrome, false 
    /// otherwise</returns>
    public bool IsMatch()
    {
      if (string.IsNullOrEmpty(input))
      {
        return true;
      }
      leftCursorIndex = -1;
      rightCursorIndex = input.Length;
      printer.WriteInitialPositions(leftCursorIndex, rightCursorIndex);

      bool symbolsEquals;
      do
      {
        symbolsEquals = NextStep();
        if (leftCursorIndex == rightCursorIndex)
        {
          break;
        }
      } while (symbolsEquals);

      return symbolsEquals;
    }
    #endregion

  }
}
